#!/usr/bin/env python
################################################################################
#            __  __            _   ____        _ _ ____        _              #
#           |  \/  | ___  __ _| |_| __ )  __ _| | | __ )  ___ | |_           #
#           | |\/| |/ _ \/ _` | __|  _ \ / _` | | |  _ \ / _ \| __|         #
#           | |  | |  __/ (_| | |_| |_) | (_| | | | |_) | (_) | |_          #
#           |_|  |_|\___|\__,_|\__|____/ \__,_|_|_|____/ \___/ \__|          #
#                                                                             #
#                                     by                                       #
# _______ .______       _______  _______   ______   ______    _______   _______ #
#|   ____||   _  \     |   ____||   ____| /      | /  __  \  |       \ |   ____| #
#|  |__   |  |_)  |    |  |__   |  |__   |  ,----'|  |  |  | |  .--.  ||  |__     #
#|   __|  |      /     |   __|  |   __|  |  |     |  |  |  | |  |  |  ||   __|     #
#|  |     |  |\  \----.|  |____ |  |____ |  `----.|  `--'  | |  '--'  ||  |____     #
#|__|     | _| `._____||_______||_______| \______| \______/  |_______/ |_______|   #
#                                                                                 #
#                            Channel Mood Module                                 #
#                             Modular FrameWork                                 #
#
# Coders
#
#	Justin Giorgi (rebel_kid)
#
# Debuggers
#
#	Justin Giorgi (rebel_kid)
#	Lachlan Gilmour (chilli0)
#	David Liedle (spiderbyte)
#	(Polarina)
#	(prologic)
#
################################################################################
#                                                                              #
#                               GPL Licensed                                   #
#                                                                              #
################################################################################

# Imports
from decimal import *
import Levenshtein as compare
import time

# Manages UserMood instances and their nick/channel
class ChanMood:
	
	# Creates the nicklist
	def __init__(self):
		self.nicklist = {}
	
	# Checks if an instance exists and calls either create new or pass to existing functions
	def message(self, globalvars, msgvars):
		if (msgvars["chanid"], msgvars["nick"]) in self.nicklist.keys():
			self._passit(globalvars, msgvars)
		else:
			self._create(globalvars, msgvars)
	
	# Passes to exhisting UserMood instance
	def _passit(self, globalvars, msgvars):
		if msgvars["features"][7] != "00000":
			source = msgvars["nick"]
			message = msgvars["message"]
			self.nicklist[(msgvars["chanid"], msgvars["nick"])].message(globalvars, msgvars, source, message)
	
	# Creates new UserMood instance and passes to it
	def _create(self, globalvars, msgvars):
		if msgvars["features"][7] != "00000":
			self.nicklist[(msgvars["chanid"], msgvars["nick"])] = UserMood(msgvars["features"][7])
			self._passit(self, globalvars, msgvars)

# Monitors a users mood
class UserMood:

	# Sets startup variables
	def __init__(self, settings):
		getcontext().prec = 2
		self.points = 0
		self.time = 0
		self.cursed = False
		self.caped = False
		self.repeated = False
		self.fasted = False
		self.lastmessage = ""
		
		# Finds which functions to ignore and which to check
		settings = map(int, settings)
		ignores = int(str(settings[0]) + str(settings[1]))
		whenwarn = settings[2]
		whenkick = settings[3]
		whenban = settings[4]
		if ignores == 1:
			self.repeating = True
			self.fast = False
			self.cussing = False
			self.caps = False
		elif ignores == 5:
			self.repeating = False
			self.fast = True
			self.cussing = False
			self.caps = False
		elif ignores == 6:
			self.repeating = True
			self.fast = True
			self.cussing = False
			self.caps = False
		elif ignores == 9:
			self.repeating = False
			self.fast = False
			self.cussing = True
			self.caps = False
		elif ignores == 10:
			self.repeating = True
			self.fast = False
			self.cussing = True
			self.caps = False
		elif ignores == 11:
			self.repeating = False
			self.fast = False
			self.cussing = False
			self.caps = True
		elif ignores == 12:
			self.repeating = True
			self.fast = False
			self.cussing = False
			self.caps = True
		elif ignores == 14:
			self.repeating = False
			self.fast = True
			self.cussing = True
			self.caps = False
		elif ignores == 15:
			self.repeating = True
			self.fast = True
			self.cussing = True
			self.caps = False
		elif ignores == 16:
			self.repeating = False
			self.fast = True
			self.cussing = False
			self.caps = True
		elif ignores == 17:
			self.repeating = True
			self.fast = True
			self.cussing = False
			self.caps = True
		elif ignores == 20:
			self.repeating = False
			self.fast = False
			self.cussing = True
			self.caps = True
		elif ignores == 21:
			self.repeating = True
			self.fast = False
			self.cussing = True
			self.caps = True
		elif ignores == 25:
			self.repeating = False
			self.fast = True
			self.cussing = True
			self.caps = True
		elif ignores == 26:
			self.repeating = True
			self.fast = True
			self.cussing = True
			self.caps = True
			
		# Checks on which offense(s) to warn
		if whenwarn == 0:
			self.warn = [0,0,0]
		elif whenwarn == 1:
			self.warn = [1,0,0]
		elif whenwarn == 2:
			self.warn = [0,1,0]
		elif whenwarn == 3:
			self.warn = [1,1,0]
		elif whenwarn == 4:
			self.warn = [0,0,1]
		elif whenwarn == 5:
			self.warn = [1,0,1]
		elif whenwarn == 6:
			self.warn = [0,1,1]
		elif whenwarn == 7:
			self.warn = [1,1,1]
		
		# Checks on which offense(s) to kick
		if whenkick == 0:
			self.kick = [0,0,0]
		elif whenkick == 1:
			self.kick = [1,0,0]
		elif whenkick == 2:
			self.kick = [0,1,0]
		elif whenkick == 3:
			self.kick = [1,1,0]
		elif whenkick == 4:
			self.kick = [0,0,1]
		elif whenkick == 5:
			self.kick = [1,0,1]
		elif whenkick == 6:
			self.kick = [0,1,1]
		elif whenkick == 7:
			self.kick = [1,1,1]
		
		# Checks on which offense(s) to ban
		if whenban == 0:
			self.ban = [0,0,0]
		elif whenban == 1:
			self.ban = [1,0,0]
		elif whenban == 2:
			self.ban = [0,1,0]
		elif whenban == 3:
			self.ban = [1,1,0]
		elif whenban == 4:
			self.ban = [0,0,1]
		elif whenban == 5:
			self.ban = [1,0,1]
		elif whenban == 6:
			self.ban = [0,1,1]
		elif whenban == 7:
			self.ban = [1,1,1]
	
	# Calls earn point functions and adds the points then checks if user has reached an offense	
	def message(self, globalvars, msgvars, source, message):
		self.globalvars = globalvars
		self.msgvars = msgvars
		self.points += self._cursing(message)
		self.points += self._caps(message)
		self.points += self._repeats(message)
		self.points += self._fast(message)
		if time.time() - self.time >= 60:
			self.points -= int((time.time() - self.time) / 60)
		if self.points >= 40:
			self._thirdoffense(source)
		elif self.points >= 25:
			self._secondoffense(source)
		elif self.points >= 15:
			self._firstoffense(source)
	
	# Checks for swear words
	def _cursing(self, message):
		if self.cussing:
		
			# Replaces swear words with a code (one that is unlikely to be sent in random conversation)
			message = message.lower().split(' ')
			for word in message:
				if word in self.curses:
					index = message.index(word)
					message[index] == "H7noUjX8G9AQWEH"
			
			# Counts the number of swear words and divides them by the number of total words (rounds to ten if less than ten) to reach a percentage of swears
			count = message.count("H7noUjX8G9AQWEH")
			wordcount = len(message)
			if wordcount < 10:
				wordcount = 10
			swore = int(str(Decimal(count) / Decimal(wordcount)).strip("0.").strip(".00"))
			
			# Checks percentage of swears and adds points
			points = 0
			if swore == 100:
				points += 15
			elif swore >= 80:
				points += 8
			elif swore >= 60:
				points += 5
			elif swore >= 40:
				points += 3
			elif swore > 10:
				points += 2
				
			# Checks if user swore, then doubles points if they swore last message as well
			if points > 0:
				if self.cursed:
					points = points * 2
				self.cursed = True
			else:
				self.cursed = False
				
			# Returns points to add
			return points
		
		return 0
	
	# Checked for capped letters
	def _caps(self, message):
		if self.caps:
		
			# Splits the message into letters and replaces capped letters with "CAPPED" (each item is 1 letter so an item == "CAPPED" is not possible)
			message = map(str, message)
			for letter in message:
				if letter == letter.upper():
					index = message.index(letter)
					message[index] = "CAPPED"
					
			# Counts the number of capped letters divides them by the total number of letters (if less then 25 it rounds to 25 )to reach a percentage 
			count = message.count("CAPPED")
			lettercount = len(message)
			if lettercount < 25:
				lettercount = 25
			caps = int(str(Decimal(count) / Decimal(lettercount)).strip("0.").strip(".00"))
			points = 0
			
			# Checks the percentage of capped and adds points
			if caps == 100:
				points += 15
			elif caps >= 65:
				points += 7
				
			# Checks if user capped, then doubles points if they capped last message as well
			if points > 0:
				if self.capped:
					points = points * 2
				self.capped = True
			else:
				self.capped = False
				
			# Returns points to add
			return points
		
		return 0
	
	def _repeat(self, message):
		if self.repeating:
		
			# Calculates the "distance" or number of single character replace functions required to make the strings equal between the current and last message
			distance = compare.distance(message.lower(), self.lastmessage.lower())
			message = message.split()
			
			# Calculates the percentage of words that have an altered character (percentages higher than 100 are expected as the norm, a higher percentage means LESS of a match has occured
			matches = int(str(Decimal(distance) / Decimal(len(message))).strip("0.".strip(".00"))
			
			# Checks percentage of words that had an altered character and adds points
			points = 0
			if matches < 300:
				points += 3
			elif matches < 200:
				points += 5
			elif matches < 100:
				points += 10
				
			# Checks if user repeated, then doubles points if they repeated last message as well
			if points > 0:
				if self.repeated:
					points = points * 2
				self.repeated = True
			else:
				self.repeated = False
				
			# Returns points to add
			return points	
		
		return 0
		
	def _fast(self, message):
		if self.fast:
			
			# Checks if messages were sent quickly (based on stored time of last message) and adds points
			points = 0
			if time.time() - self.time <= 4:
				points += 2
			elif time.time() - self.time <= 2:
				points += 4
			elif time.time() - self.time <= 1:
				points += 6
				
			# Checks if user sent messages quickly and doubles points if they did so with the last message as well
			if points > 0:
				if self.fasted:
					points = points * 2
				self.fasted = True
			else:
				self.fasted = False
				
			# Returns points to add
			return points
		
		return 0
		
	def _firstoffense(self, source):
		if self.warn[0]:
			self._warn(source, 1)
		elif self.kick[0]:
			self._kick(source, 1)
		elif self.ban[0]:
			self._ban(source)
		
	def _secondoffense(self, source):
		if self.warn[1]:
			self._warn(source, 2)
		elif self.kick[1]:
			self._kick(source, 2)
		elif self.ban[1]:
			self._ban(source)
	
	def _thirdoffense(self, source):
		if self.warn[2]:
			self._warn(source, 3)
		elif self.kick[2]:
			self._kick(source, 3)
		elif self.ban[2]:
			self._ban(source)
	
	def _warn(self, source, offense):
		if not msgvars["accessperms"][8] and msgvars["nickid"] not in globalvars["identified"]:
			if offense == 1:
				msgvars["server"].privmsg(msgvars["channel"], "%s: Please calm down. If you do not, further action will be taken." % (source))
			elif offense == 2:
				msgvars["server"].privmsg(msgvars["channel"], "%s: You are disrupting the channel stop immediately." % (source))
			elif offense == 3:
				msgvars["server"].privmsg(msgvars["channel"], "%s: STOP NOW! You are disrupting the channel." % (source))
		
	def _kick(self, source, offense):
		if not msgvars["accessperms"][8] and msgvars["nickid"] not in globalvars["identified"]:
			if offense == 1:
				msgvars["server"].kick(msgvars["channel"], source, "You were disrupting tha channel. This is your warning.")
			elif offense == 2:
				msgvars["server"].kick(msgvars["channel"], source, "You were disrupting tha channel. This is your FINAL warning.")
			elif offense == 3:
				msgvars["server"].kick(msgvars["channel"], source, "Your behavior will not be tolerated.")
		
	def _ban(self, source):
		if not msgvars["accessperms"][8] and msgvars["nickid"] not in globalvars["identified"]:
			msgvars["server"].mode(msgvars["channel"], "+b *!%s" % (msgvars["host"]))
			msgvars["server"].kick(msgvars["channel"], source, "You have been banned from the channel.")
		
def autoload(globalvars):
	
	# Creates the instance
	chanmood = ChanMood()
	
	# Loads globalalways
	globalalways = {'chanmood':chanmood.message}
	for feature in globalalways.items():
		if feature[0] not in globalvars["globalalways"].keys():
			globalvars["globalalways"][feature[0]] = feature[1]
	
def load(globalvars, msgvars):
	
	# Creates the instance
	chanmood = ChanMood()
	
	# Loads globalalways
	globalalways = {'chanmood':chanmood.message}
	for feature in globalalways.items():
		if feature[0] not in globalvars["globalalways"].keys():
			globalvars["globalalways"][feature[0]] = feature[1]
		else:
			msgvars["server"].privmsg(msgvars["destination"], "There is already a function named %s." % (feature[0]))
	return True
			
def unload(globalvars, msgvars, reloadmodule = False):
	
	# Unloads globalalways
	globalalways = {'chanmood':chanmood.message}
	for feature in globalalways.items():
		if feature[0] in globalvars["globalalways"].keys():
			del globalvars["globalalways"][feature[0]]
	return True
